
#define FMT_HEADER_ONLY


#include <iostream>
#include <sstream>
#include <string>
#include <random>

#include <chrono>

#include "../deps/fmt/include/fmt/format.h"
#include "../include/Orderbook.h"

using namespace mdtx::punch;

class print_listener : public OrderbookListener
{
private:
    std::string describe(const Order &order)
    {
        std::string side_str = order.side == OrderSideBuy ? "BUY " : "SELL";
        std::string ioc_str = order.ioc ? "IOC" : "   ";
        std::string aon_str = order.aon ? "AON" : "   ";
        std::string type_str = order.type == OrderTypeMarket ? "MKT" : "LMT";

        std::string str = fmt::format("{} ({},{}) {} {: >4d}@{: <4d}", type_str, ioc_str, aon_str, side_str, order.quant, order.price);

        return str;
    }

public:
    std::uint64_t count;
    virtual void match(std::uint64_t instrument, std::uint64_t seq_id,
                       // after trade
                       const Order &order, const Order &resting_order,
                       // aggressor side, matched price, matched quantity
                       OrderSide side, std::int64_t price, std::int64_t quant)
    {
        std::string side_str = side == OrderSideBuy ? "   <BUY>" : "  <SELL>";
        std::string str = fmt::format("MATCH  {} -> {} : {}{: >4d}@{: <4d}", describe(order), describe(resting_order), side_str, quant, price);
        std::cout << str << std::endl;
    }

    virtual void queue(std::uint64_t instrument, std::uint64_t seq_id,
                       // can be none or partial
                       const Order &order)
    {
        std::cout << "QUEUE  " << describe(order) << std::endl;
        ++count;
    }

    virtual void cancel(std::uint64_t instrument, std::uint64_t seq_id,
                        const Order &order,
                        std::int64_t quant, std::int64_t remain)
    {
        std::string str = fmt::format("CANCEL {} : {: >4d} -> {: >4d}", describe(order), quant, remain);
        std::cout << str << std::endl;
    }

    void submit(const Order &order)
    {
        std::cout << "SUBMIT " << describe(order) << std::endl;
    }
};

class dummy_listener : public OrderbookListener
{
public:
    virtual void match(std::uint64_t instrument, std::uint64_t seq_id,
                       // after trade
                       const Order &order, const Order &resting_order,
                       // aggressor side, matched price, matched quantity
                       OrderSide side, std::int64_t price, std::int64_t quant)
    {
    }

    virtual void queue(std::uint64_t instrument, std::uint64_t seq_id,
                       // can be none or partial
                       const Order &order)
    {
    }

    virtual void cancel(std::uint64_t instrument, std::uint64_t seq_id,
                        const Order &order,
                        std::int64_t quant, std::int64_t remain)
    {
    }

    void submit(const Order &order)
    {
    }
};

int main()
{

    int seed = 1;
    int norder = 100000000;

    std::default_random_engine eng(seed);
    std::uniform_int_distribution<std::int8_t> rand_side(0, 1);
    std::uniform_int_distribution<std::int8_t> rand_type(0, 1);
    std::uniform_int_distribution<std::int64_t> rand_price_buy(10, 50), rand_price_sell(15, 55);
    std::uniform_int_distribution<std::int64_t> rand_quant(100, 500);
    std::uniform_int_distribution<int> rand_ioc(0, 2), rand_aon(0, 2);

    //std::shared_ptr<print_listener> listener = std::make_shared<print_listener>();
    std::shared_ptr<dummy_listener> listener = std::make_shared<dummy_listener>();
    Orderbook book(1, listener);

    auto order = book.submit(OrderSideBuy, OrderTypeLimit, 20, 150);
    book.cancel(order.seq_id, 100);
    book.cancel(12345, 100);

    auto tstart = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < norder; ++i)
    {
        // generate dummy order
        OrderSide side = rand_side(eng);
        std::int64_t price = side == OrderSideBuy ? rand_price_buy(eng) : rand_price_sell(eng);
        Order order{0, price, rand_quant(eng), side, rand_type(eng), 0, false, !rand_ioc(eng), !rand_aon(eng)};
        // submit order
        listener->submit(order);
        book.submit(order.side, order.type, order.price, order.quant, order.gtc, order.ioc, order.aon);
    }

    auto tend = std::chrono::high_resolution_clock::now();

    std::cout << "Delta T : " << (tend - tstart).count() * 1.0e-6 << "ms" << std::endl;
}
